<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>分片上传</title>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <!-- 地址： https://juejin.cn/post/7177045936298786872 -->
</head>

<body>
  <input type="file" id="input">
  <button id="upload">上传</button>
  <!-- 上传进度 -->
  <div style="width: 300px" id="progress"></div>
  <script>
    let input = document.getElementById('input')
    let upload = document.getElementById('upload')
    let files = {}//创建一个文件对象
    let chunkList = []//存放切片的数组
    let FRAGM_SIZE = 10 * 1024;//分片大小

    // （1）读取文件
    input.addEventListener('change', (e) => {
      files = e.target.files[0]
      console.log(files);

      //（2）创建切片
      chunkList = createChunk(files, FRAGM_SIZE)
      console.log(chunkList);
    })

    /** 
     * 创建切片，大小单位kb（1M = 1*1024*1024）
     * 核心思想：切为指定大小的数组，
     * 返回数组，数组长度即是请求个数,预计分成8个
     * 两个形参：files是大文件，size是切片的大小，单位kb，
     */
    function createChunk(files, size = 10 * 1024 * 1024) {
      const chunkList = []
      let current  = 0
      while (current  < files.size) {
        chunkList.push({ file: files.slice(current , current  + size) });//使用File对象的原型方法slice()进行切片
        current += size
      }
      return chunkList
    }
    // （3）上传切片，数据处理
    async function uploadFile(list) {
      const requestList = list.map(({ file, fileName, index, chunkName }) => {
        const formData = new FormData() // 创建表单类型数据 （如果它的字段类型不是 Blob 也不是 File，则会被转换成字符串类。）
        formData.append('file', file)//该文件
        formData.append('fileName', fileName)//文件名
        formData.append('chunkName', chunkName)//切片名
        return { formData, index }
      })
        .map(({ formData, index }) => axiosRequest({
          method: 'post',
          url: 'http://localhost:3000/upload',//请求接口，要与后端一一一对应
          data: formData
        })
          .then(res => {
            console.log(res);
            //显示每个切片上传进度
            let p = document.createElement('p')
            p.innerHTML = `${list[index].chunkName}--${res.data.message}`
            document.getElementById('progress').appendChild(p)
          })
        )
      //保证所有的切片都已经传输完毕
      await Promise.all(requestList)
      console.log('开始请求合并');
      alert('asda')
      //调用函数，当所有切片上传成功之后，通知后端合并
      merge(files.name, FRAGM_SIZE)
    }

    //请求函数
    function axiosRequest({ method = "post", url, data }) {
      return new Promise((resolve, reject) => {
        const config = {//设置请求头
          headers: 'Content-Type:application/x-www-form-urlencoded',
        }
        //默认是post请求，可更改
        axios[method](url, data, config).then((res) => {
          resolve(res)
        })
      })
    }

    // 文件上传
    upload.addEventListener('click', () => {
      const uploadList = chunkList.map(({ file }, index) => ({
        file,
        size: file.size,
        percent: 0,
        chunkName: `${files.name}-${index}`,
        fileName: files.name,
        index
      }))
      //发请求，调用函数
      uploadFile(uploadList)
    })

    // （2）前端通知，合并切片
    // 后端返回切片上传成功，前端通知后端去做切片合并，传入分片大小
    function merge(fileName, size = 10 * 1024 * 1024) {
      axiosRequest({
        method: 'post',
        url: 'http://localhost:3000/merge',//后端合并请求
        data: JSON.stringify({
          size,
          fileName
        }),
      })
    }
  </script>
</body>

</html>